package com.xsf.jieyou.business.controller;

import com.xsf.jieyou.business.BusinessException;
import com.xsf.jieyou.business.common.dto.ResponseResult;
import com.xsf.jieyou.business.common.dto.ResponseStatus;
import com.xsf.jieyou.business.dto.RegisterParam;
import com.xsf.jieyou.cloud.api.CloudRedisSmsService;
import com.xsf.jieyou.cloud.api.CloudSmsService;
import com.xsf.jieyou.cloud.dto.SmsParam;
import com.xsf.jieyou.commons.utils.IDUtils;
import com.xsf.jieyou.commons.utils.RegexUtils;
import com.xsf.jieyou.provider.api.ProviderTbUserRoleService;
import com.xsf.jieyou.provider.api.ProviderTbUserService;
import com.xsf.jieyou.provider.domain.TbUser;
import com.xsf.jieyou.provider.domain.TbUserRole;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.BeanUtils;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
 * 注册服务,业务控制层。
 * <p>
 * Description:TODO
 * </p>
 *
 * @author xsf
 * @version v1.0.0
 * @date 2019/11/18 0018 4:38
 * @see com.xsf.jieyou.business.controller
 **/
@RestController
@RequestMapping(value = "/user/register")
@RefreshScope
@Slf4j
public class RegisterController {
    @Reference(version = "1.0.0")
    private ProviderTbUserService providerTbUserService;
    @Reference(version = "1.0.0")
    private ProviderTbUserRoleService providerTbUserRoleService;
    @Reference(version = "1.0.0")
    private CloudRedisSmsService cloudRedisSmsService;
    @Reference(version = "1.0.0")
    private CloudSmsService cloudSmsService;
    /**
     * 默认用户角色：USER
     */
    private static final long DEFAULT_USER_ROLE = 3;
    /**
     * 发送短信成功返回信息   Message:OK
     */
    private static final String SMS_MESSAGE_SUCCESS = "OK";
    @Resource
    private BCryptPasswordEncoder passwordEncoder;


    /**
     * 校验用户名唯一性
     *
     * @return {@link ResponseResult}
     */
    @GetMapping(value = "username/{username}")
    public ResponseResult<?> validateUsername(@PathVariable String username) {
        // 1、格式验证，失败抛出自定义异常
        this.validateUsernameFormat(username);
        // 2、唯一性验证，失败抛出自定义异常
        this.validateUsernameOnly(username);
        // 3、通过
        return ResponseResult.ok();
    }

    /**
     * 校验手机号唯一性
     *
     * @return {@link ResponseResult}
     */
    @GetMapping(value = "phone/{phone}")
    public ResponseResult<?> validatePhoneNum(@PathVariable String phone) {
        // 1、格式验证，失败抛出自定义异常
        this.validatePhoneFormat(phone);
        // 2、唯一性验证，失败抛出自定义异常
        this.validatePhoneOnly(phone);
        // 3、通过
        return ResponseResult.ok();
    }

    /**
     * 手机验证码验证
     *
     * @param phone 手机号
     * @param code  验证码
     * @return {@link ResponseResult}
     */
    @GetMapping(value = "sms/{phone}/{code}")
    public ResponseResult<?> checkPhoneCode(@PathVariable String phone, @PathVariable String code) {
        // 1、格式验证，失败抛出自定义异常
        this.validatePhoneFormat(phone);
        this.validatePhoneCodeFormat(code);

        // 2、唯一性验证，失败抛出自定义异常
        this.validatePhoneOnly(phone);

        // 3、手机验证码验证
        this.validatePhoneCode(phone, code);

        // 4、通过
        return ResponseResult.ok();
    }

    @GetMapping(value = "sms/{phone}")
    public ResponseResult<?> sendRegisterSms(@PathVariable String phone) {
        // 1、格式验证，失败抛出自定义异常
        this.validatePhoneFormat(phone);
        // 2、唯一性验证，失败抛出自定义异常
        this.validatePhoneOnly(phone);

        // 3、通过，发送SMS
        try {
            SmsParam smsParam = this.cloudSmsService.sendRegisterSms(phone);
            if (smsParam != null) {
                if (SMS_MESSAGE_SUCCESS.equals(smsParam.getMessage())) {
                    return ResponseResult.ok();
                }
                return ResponseResult.build(ResponseStatus.SEND_SMS_ERROR.code(), smsParam.getMessage());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        throw new BusinessException(ResponseStatus.SEND_SMS_ERROR);
    }

    /**
     * 用户注册，手机验证码
     *
     * @return {@link ResponseResult}
     */
    @PostMapping(value = "")
    public ResponseResult<?> register(@RequestBody RegisterParam registerParam) {
        // 1、参数验证，失败抛出自定义异常
        this.validateRegisterParam(registerParam);

        // 2、唯一性验证，失败抛出自定义异常
        this.validatePhoneOnly(registerParam.getPhone());
        this.validateUsernameOnly(registerParam.getUsername());
        this.validatePhoneCodeFormat(registerParam.getCode());

        // 3、验证码验证
        this.validatePhoneCode(registerParam.getPhone(), registerParam.getCode());

        // 4、新增用户
        TbUser tbUser = new TbUser();
        long userId = IDUtils.getId();
        tbUser.setId(userId);
        BeanUtils.copyProperties(registerParam, tbUser);

        // 5、插入角色，默认 USER
        TbUserRole tbUserRole = new TbUserRole();
        tbUserRole.setUserId(userId);
        tbUserRole.setRoleId(DEFAULT_USER_ROLE);
        int roleFlag = this.providerTbUserRoleService.insertTbUserRole(tbUserRole);

        // ******************测试 Seata 分布式事务方案 ************************
        // int i = 1 / 0;
        // ******************测试 Seata 分布式事务方案 ************************

        // 6、插入用户
        int userFlag = this.providerTbUserService.insert(tbUser);
        // 1：新增成功    else 新增失败
        return (roleFlag == 1 && userFlag == 1) ?
                ResponseResult.ok() :
                ResponseResult.fail();
    }

    /**
     * 注册参数验证
     *
     * @param registerParam {@link RegisterParam} 注册参数
     */
    private void validateRegisterParam(RegisterParam registerParam) {
        if (StringUtils.isEmpty(registerParam.getUsername()) ||
                StringUtils.isEmpty(registerParam.getPhone()) ||
                StringUtils.isEmpty(registerParam.getPassword()) ||
                StringUtils.isEmpty(registerParam.getCode()) ||
                !RegexUtils.validateUsername(registerParam.getUsername()) ||
                !RegexUtils.validatePassword(registerParam.getPassword()) ||
                !RegexUtils.validatePhone(registerParam.getPhone())) {
            throw new BusinessException(ResponseStatus.REQUEST_PARAM_INVALID);
        }
    }

    /**
     * 校验用户名唯一性
     */
    private void validateUsernameOnly(String username) {
        int usernameCount = this.providerTbUserService.validateUsername(username);
        if (usernameCount != 0) {
            throw new BusinessException(ResponseStatus.USERNAME_EXIST);
        }
    }

    /**
     * 校验用户名格式
     */
    private void validateUsernameFormat(String username) {
        if (StringUtils.isEmpty(username) ||
                !RegexUtils.validateUsername(username)
        ) {
            throw new BusinessException(ResponseStatus.REQUEST_PARAM_INVALID);
        }
    }

    /**
     * 校验手机号唯一性
     */
    private void validatePhoneOnly(String phone) {
        int count = this.providerTbUserService.validatePhoneNum(phone);
        if (count != 0) {
            throw new BusinessException(ResponseStatus.PHONE_EXIST);
        }
    }

    /**
     * 校验手机号格式
     *
     * @param phone 手机号
     */
    private void validatePhoneFormat(String phone) {
        if (StringUtils.isEmpty(phone) ||
                !RegexUtils.validatePhone(phone)
        ) {
            throw new BusinessException(ResponseStatus.REQUEST_PARAM_INVALID);
        }
    }

    /**
     * 验证码验证
     *
     * @param phone 手机号
     * @param code  验证码
     */
    private void validatePhoneCode(String phone, String code) {
        // 获取Redis中的验证码（加密后的）
        String redisCode = this.cloudRedisSmsService.selectRegisterCodeFromRedis(phone);
        // 验证码比较
        boolean matches = this.passwordEncoder.matches(code, redisCode);
        // 失败，抛出自定义异常
        if (!matches) {
            throw new BusinessException(ResponseStatus.AUTH_CODE_ERROR);
        }
    }

    /**
     * 校验验证码格式
     *
     * @param code 验证码
     */
    private void validatePhoneCodeFormat(String code) {
        if (StringUtils.isEmpty(code) ||
                !RegexUtils.validatePhoneCode(code)
        ) {
            throw new BusinessException(ResponseStatus.REQUEST_PARAM_INVALID);
        }
    }
}
